package com.example.practice.db;

import java.lang.ref.WeakReference;
import java.util.ArrayList;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.os.Handler;
import android.os.Message;

/**
 * 数据库表的基础类
 * 
 * @author asus
 *
 */
public abstract class BaseDB {
	// 数据库读取消息id
	private static final int LOAD_DB_MAG_ID = 0x01;
	// 数据库存储消息id
	private static final int SAVE_DB_MAG_ID = 0x02;
	// 自定义消息id的起始值
	public static final int CUSTOM_MSG_START = SAVE_DB_MAG_ID + 1;

	// 数据库辅助类
	protected DBHelper mDBHelper;
	protected DBHandler mHandler = null;
	// 数据库读取回调函数
	private static OnDatabaseHandlerListener mLoadDBListener = null;
	// 保存数据库的回调函数
	private static OnDatabaseHandlerListener mSaveDBListener = null;

	// 线程同步锁
	protected final static byte[] _singleLock = new byte[0];
	protected final static byte[] _writeLock = new byte[0];

	/**
	 * 获取数据库表名称，由子类实现
	 * 
	 * @return 表的名字
	 */
	protected abstract String getTableName();

	/**
	 * 将Object转化成ContentValues
	 * 
	 * @param data
	 *            要转化的数据
	 * @return ContentValues
	 */
	public abstract ContentValues getContentValuesByData(Object data);

	/**
	 * 获取Object
	 * 
	 * @param cursor
	 *            游标
	 * @return Object数据
	 */
	public abstract Object getDataByCursor(Cursor cursor);

	public BaseDB(Context ctx, boolean isCreateFromUiThread) {
		synchronized (_singleLock) {
			// 因为handler只能工作于ui主线程中，如果BaseDB不是在ui主线程中创建，
			// 则不能创建handler
			if (isCreateFromUiThread) {
				mHandler = new DBHandler((Activity) ctx);
			}
			// 创建数据库辅助类
			if (mDBHelper == null) {
				mDBHelper = new DBHelper(ctx);
			}
			open();
		}
	}

	/**
	 * 只有调用了DatabaseHelper的getWritableDatabase()方法或者getReadableDatabase()方法之后，
	 * 才会创建或打开一个连接
	 * 
	 */
	private void open() {
		synchronized (_writeLock) {
			try {
				mDBHelper.getReadableDatabase();
			} catch (Exception e) {
			}

			try {
				mDBHelper.getWritableDatabase();
			} catch (Exception e) {
			}
		}
	}

	/**
	 * 关闭数据库
	 * 
	 */
	public void close() {
		synchronized (_writeLock) {
			mDBHelper.close();
		}
	}

	/**
	 * 删除所有数据
	 * 
	 * @return 数据是否已删除
	 */
	public boolean deleteAllRows() {
		synchronized (_writeLock) {
			return mDBHelper.getWritableDatabase().delete(getTableName(), null,
					null) > 0;
		}
	}

	/**
	 * 读取全部数据
	 * 
	 * @return 游标
	 * @throws SQLException
	 */
	public Cursor getAllRows() throws SQLException {
		Cursor mCursor = mDBHelper.getReadableDatabase().query(true,
				getTableName(), null, null, null, null, null, null, null);
		return mCursor;
	}

	/**
	 * 插入一条数据
	 * 
	 * @param data
	 *            插入的数据
	 * @return 数据插入成功则返回1，否则为-1
	 */
	public long insertRow(Object data) {
		long ret = -1;
		if (data != null) {
			ContentValues initialValues = getContentValuesByData(data);
			if (mDBHelper.getWritableDatabase().insert(getTableName(), null,
					initialValues) > 0) {
				ret = 1;
			} else {
				ret = -1;
			}
		}
		return ret;
	}

	/**
	 * 获取数据
	 * 
	 * @param list
	 *            存储读出来的数据
	 * @param selection
	 *            读取数据的限制条件
	 * @return 是否查询成功
	 */
	private boolean loadDataFormDBEnter(ArrayList<Object> list, String selection) {
		boolean ret = true;
		
		// 调用读取之前的回调函数
		onLoadDataFormDBbefore();
		if (list != null) {
			Cursor contactCursor = null;
			try {
				contactCursor = mDBHelper.getReadableDatabase().query(true,
						getTableName(), null, selection, null, null, null,
						null, null);
				if (contactCursor != null) {
					if (contactCursor.moveToFirst()) {
						do {
							// 从游标中解析出java对象并插入到list中
							Object data = getDataByCursor(contactCursor);
							if (data != null) {
								list.add(data);
							}
						} while (contactCursor.moveToNext());
					}
				}
			} catch (Exception e) {
				ret = false;
				e.printStackTrace();
			} finally {
				if (contactCursor != null) {
					contactCursor.close();
				}
			}
		}
		// 调用读取之后的回调函数
		onLoadDataFormDBAfter();
		return ret;
	}

	/**
	 * 该函数一般由主线程调用，但我们也会在内部专门启动一个线程来读取数据，如果出现调用者不是ui主线程的情况则在该线程中执行函数，不必另启新线程
	 * 
	 * @param selection
	 *            条件限制
	 * @param listener
	 *            数据读取的回调函数
	 */
	public void loadDataFromDB(String selection,
			OnDatabaseHandlerListener listener) {
		mLoadDBListener = listener;
		// 如果有handler，则通过给handler发送消息的方式来执行回调函数
		if (mHandler != null) {
			final String fSelection = selection;
			// 开启线程
			new Thread() {
				@Override
				public void run() {
					super.run();
					ArrayList<Object> list = new ArrayList<Object>();
					// 正式开始从数据库读取数据
					boolean isSuccess = loadDataFormDBEnter(list, fSelection);
					// 将结果发送给handler处理
					Message msg = Message.obtain();
					msg.what = LOAD_DB_MAG_ID;
					msg.obj = list;
					msg.arg1 = isSuccess ? 1 : 0;
					mHandler.sendMessage(msg);
				}
			}.start();
		} else {
			// 如果调用者不是ui主线程，则直接在该线程中执行，不必另启动线程
			ArrayList<Object> list = new ArrayList<Object>();
			boolean isSuccess = loadDataFormDBEnter(list, selection);
			if (listener != null) {
				listener.onDatabaseHandler(list, isSuccess);
			}
		}
	}

	/**
	 * 保存数据记录
	 * 
	 * @param list
	 *            存放要存储的数据
	 * @return 数据是否保存
	 */
	private boolean saveDataToDBEnter(ArrayList<Object> list) {
		boolean ret = true;
		// 调用存储之前的回调函数
		onSaveDataToDBbefore();
		if (list != null) {
			try {
				for (int i = 0; i < list.size(); i++) {
					// 插入一条数据
					insertRow(list.get(i));
				}
			} catch (Exception e) {
				ret = false;
				e.printStackTrace();
			}
		}
		// 调用存储之后的回调函数
		onSaveDataToDBAfter();
		return ret;
	}

	/**
	 * 将一组对象数据保存到数据库，异步方法，操作结束以后通过listener接口通知调用者
	 * 
	 * @param list
	 *            保存的数据列表
	 * @param listener
	 *            监听器
	 */
	public void saveDataToDB(ArrayList<Object> list,
			OnDatabaseHandlerListener listener) {
		if (list != null && list.size() > 0) {
			mSaveDBListener = listener;
			final ArrayList<Object> fList = list;
			if (mHandler != null) {
				// 开启线程
				new Thread() {
					@Override
					public void run() {
						super.run();
						boolean isSuccess = true;
						// 向数据库中存储数据记录
						isSuccess = saveDataToDBEnter(fList);
						// 将结果发送给handler处理
						Message msg = Message.obtain();
						msg.what = SAVE_DB_MAG_ID;
						msg.obj = null;
						msg.arg1 = isSuccess ? 1 : 0;
						mHandler.sendMessage(msg);
					}
				}.start();
			} else {
				// 如果调用者不是ui主线程，则直接在该线程中执行，不必另启动线程
				boolean isSuccess = true;
				isSuccess = saveDataToDBEnter(fList);
				if (listener != null) {
					listener.onDatabaseHandler(null, isSuccess);
				}
			}
		}
	}

	/**
	 * 用来处理用户自定义的消息
	 * 
	 * @param msg
	 *            消息对象
	 */
	protected static void onDBHandleMessage(Message msg) {
	}

	/**
	 * 在数据读取时调用的函数
	 * 
	 */
	protected void onLoadDataFormDBbefore() {
	}

	/**
	 * 在数据读取后调用的函数
	 * 
	 */
	protected void onLoadDataFormDBAfter() {
	}

	/**
	 * 在数据保存前调用的函数
	 * 
	 */
	protected void onSaveDataToDBbefore() {
	}

	/**
	 * 在数据保存后调用的函数
	 * 
	 */
	protected void onSaveDataToDBAfter() {
	}

	/**
	 * 主要接受子线程发送的数据, 并调用主线程的回调函数，用此数据配合主线程更新UI.
	 * 
	 * @author asus
	 *
	 */
	private static class DBHandler extends Handler {
		private final WeakReference<Activity> mActivity;

		public DBHandler(Activity activity) {
			mActivity = new WeakReference<Activity>(activity);
		}

		@Override
		public void handleMessage(Message msg) {
			if (mActivity.get() == null) {
				return;
			}
			switch (msg.what) {
			case LOAD_DB_MAG_ID:
				if (mLoadDBListener != null) {
					// 调用主线程的数据库读取回调函数
					boolean isSuccess = (msg.arg1 >= 1) ? true : false;
					ArrayList<Object> list = (ArrayList<Object>) (msg.obj);
					mLoadDBListener.onDatabaseHandler(list, isSuccess);
				}
				break;
			case SAVE_DB_MAG_ID:
				if (mSaveDBListener != null) {
					// 调用主线程的数据库存储回调函数
					boolean isSuccess = (msg.arg1 >= 1) ? true : false;
					mLoadDBListener.onDatabaseHandler(null, isSuccess);
				}
				break;

			default:
				// 用来处理用户自定义的消息
				onDBHandleMessage(msg);
				break;
			}
		}
	}

	/**
	 * 监听接口
	 * 
	 * @author asus
	 *
	 */
	public interface OnDatabaseHandlerListener {
		/**
		 * 数据库操作结果的接口
		 * 
		 * @param dataList
		 *            一般用来存储读取的数据
		 * @param isSuccess
		 *            一般用来表示本次操作是否成功
		 * @return 监听是否成功
		 */
		boolean onDatabaseHandler(ArrayList<Object> dataList, boolean isSuccess);
	}
}
